/*=============================================================================
*
*   Copyright (C) 2020 All rights reserved.
*
*   Filename:		test_shape.cpp
*
*   Author: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Date:  2020-01-17 16:54
*
*   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Last modified:	2020-08-04 01:47
*
*   Description:
*
=============================================================================*/
#define BOOST_AUTO_TEST_MAIN

#define TEST_POINT2D
#define TEST_VERTEX2D
#define TEST_SEGMENT2D
#define TEST_TRIANGLE2D
#define TEST_QUADRANGLE2D
#define TEST_POINT3D
#define TEST_FACTORY
#define TEST_SHAPE_FUNCTION

#include <boost/test/included/unit_test.hpp>
#include <iostream>

#ifdef TEST_POINT2D
#include "../include/geometry/shape/point.hpp"
#endif

#ifdef TEST_VERTEX2D
#include "../include/geometry/shape/vertex.hpp"
#endif

#ifdef TEST_SEGMENT2D
#include "../include/geometry/shape/segment.hpp"
#endif

#ifdef TEST_TRIANGLE2D
#include "../include/geometry/shape/triangle.hpp"
#endif

#ifdef TEST_QUADRANGLE2D
#include "../include/geometry/shape/quadrangle.hpp"
#endif

#ifdef TEST_FACTORY
#include "../include/util/factory.hpp"
#endif

using namespace geoxfem::geometry;
using namespace geoxfem;

LOGGING_REGISTER()

BOOST_AUTO_TEST_CASE(geometry_test) {
  /*  detail, trace, process, info, warning, debug, error }; */
  geoxfem::log::log_init(geoxfem::log::severity_level::info);
  using Shape      = ShapeInterface<plane_stress, mpl1>;
  using Vertex     = Vertex<plane_stress, mpl1>;
  using Segment    = Segment<plane_stress, mpl1>;
  using Triangle   = Triangle<plane_stress, mpl1>;
  using Quadrangle = Quadrangle<plane_stress, mpl1>;

  GEOXFEM_REGISTER_DERIVED(Shape, Vertex);
  GEOXFEM_REGISTER_DERIVED(Shape, Segment);
  GEOXFEM_REGISTER_DERIVED(Shape, Triangle);
  GEOXFEM_REGISTER_DERIVED(Shape, Quadrangle);

/*point class test{{{*/
#ifdef TEST_POINT2D
  {
    Point2D p1{1.3f, 6.4f}, p2{0.0f, 4.0f}, p3{3.0f, 0.0f};

    /// check instanciation
    BOOST_CHECK(&p1);
    /// check coordinates
    BOOST_CHECK(p1 == Point2D(1.3f, 6.4f));
    /// check comparable_distance
    BOOST_CHECK(boost::geometry::comparable_distance(p2, p3) == 25.0f);
  }
#endif
  /*}}}*/
/*3D point class test{{{*/
#ifdef TEST_POINT3D
  {
    Point3D p1{0.0f, 0.0f, 0.0f}, p2{0.0f, 3.0f, 4.0f}, p3{1.0f, 0.0f, 4.0f},
      p4{1.0f, 3.0f, 0.0f};
    /// check instanciation
    BOOST_CHECK(&p2);
    /// check coordinates
    BOOST_CHECK(p2.x() == 0);
    BOOST_CHECK(p2.y() == 3);
    BOOST_CHECK(p2.z() == 4);
    /// check comparable_distance
    BOOST_CHECK(boost::geometry::comparable_distance(p1, p2) == 25);
  }
#endif
  /*}}}*/
/*vertex class test{{{*/
#ifdef TEST_VERTEX2D
  {
    Point2D p1{9.3f, 71.3f};
    Vertex  v1{{p1}};
    /// check instanciation
    BOOST_CHECK(&v1);
    /// check member point
    BOOST_CHECK(v1.getHeadPoint() == Point2D(9.3f, 71.3f));
  }
#endif
  /*}}}*/
/*segment class test{{{*/
#ifdef TEST_SEGMENT2D
  {
    Point2D p1(0.0f, 0.0f), p2(4.0f, 3.0f), p3(0.0f, 3.0f), p4(0.0f, 5.0f),
      p5(4.0f, 0.0f), p6(0.0f, -1.5f), p7(4.0f, 4.5f);
    Segment seg1{{p1, p2}}, seg2{{p3, p4}}, seg3{{p3, p5}};

    std::vector<Point2D> res;
    Vector2R             lc, lc1, gc, ans;
    boost::geometry::intersection(seg1, seg3, res);
    lc << -0.4f, 0.0f;
    ans << 0.3f, 0.7f;
    gc << 1.0f, 0.75f;
    lc1 << -0.5f, 0.f;

    /// check instantiation
    BOOST_CHECK(&seg1);
    /// check member point
    BOOST_CHECK(seg1.getHeadPoint() == p1);
    /// check length
    BOOST_CHECK(boost::geometry::length(seg1) == 5.0);
    /// check comparable_distance between segment and point
    BOOST_CHECK(boost::geometry::comparable_distance(seg1, p3) == 5.76);
    /// check comparable_distance between two segments
    BOOST_CHECK(boost::geometry::comparable_distance(seg1, seg2) == 5.76);
    /// check intersection between two segments
    BOOST_CHECK(boost::geometry::equals(res.front(), Point2D(2.0f, 1.5f)));
    //////////////////////////////////////////////////////////////////
    /// check shape function
    Vector2R seg1N = seg1.getStdNByLocal(lc);
    BOOST_CHECK_MESSAGE(seg1N == ans, "segment 1 shape function\n"
                                        << seg1N << " while correct ans is\n"
                                        << ans);
    BOOST_CHECK(seg1.global2local(gc) == lc1);
  }
#endif
  /*}}}*/
/*2D triangular test{{{*/
#ifdef TEST_TRIANGLE2D
  {
    Point2D p1{0.0f, 0.0f}, p2{1.0f, 0.0f}, p3{0.0f, 1.0f}, p5{0.4f, 0.4f}, p4,
      p6{0.6f, 0.6f}, p7{0.0f, 2.0f}, p8{1.0f, -1.0f}, p9{1.0f, 1.0f};
    Triangle tri1{{p1, p2, p3}};
    Segment  seg1{{p6, p7}};
    Segment  seg2{{p3, p7}};
    Segment  seg3{{p5, p7}};
    Segment  seg4{{p3, p5}};
    Segment  seg5{{p3, p8}};
    Segment  seg6{{p9, p8}};
    boost::geometry::centroid(tri1, p4);

    /// check instanciation
    BOOST_CHECK(&tri1);
    /// check coordinates
    BOOST_CHECK(boost::geometry::area(tri1) == 0.5);
    /// check center
    BOOST_CHECK(boost::geometry::equals(p4, Point2D(1. / 3., 1. / 3.)));
    /// check cover
    BOOST_CHECK(boost::geometry::covered_by(p3, tri1));
    BOOST_CHECK(boost::geometry::covered_by(p5, tri1));
    BOOST_CHECK(!boost::geometry::covered_by(p6, tri1));

    BOOST_CHECK(!boost::geometry::within(p3, tri1));
    BOOST_CHECK(boost::geometry::within(p5, tri1));
    BOOST_CHECK(!boost::geometry::within(p6, tri1));

    BOOST_CHECK(!boost::geometry::intersects(seg1, tri1));
    BOOST_CHECK(boost::geometry::intersects(seg2, tri1));
    BOOST_CHECK(boost::geometry::intersects(seg3, tri1));
    BOOST_CHECK(boost::geometry::intersects(seg4, tri1));
    BOOST_CHECK(boost::geometry::intersects(seg5, tri1));
    BOOST_CHECK(boost::geometry::intersects(seg6, tri1));

    BOOST_CHECK(!boost::geometry::covered_by(seg1, tri1));
    BOOST_CHECK(!boost::geometry::covered_by(seg2, tri1));
    BOOST_CHECK(!boost::geometry::covered_by(seg3, tri1));
    BOOST_CHECK(boost::geometry::covered_by(seg4, tri1));
    BOOST_CHECK(!boost::geometry::covered_by(seg5, tri1));
    BOOST_CHECK(!boost::geometry::covered_by(seg6, tri1));

    BOOST_CHECK(!boost::geometry::crosses(seg1, tri1));
    BOOST_CHECK(!boost::geometry::crosses(seg2, tri1));
    BOOST_CHECK(boost::geometry::crosses(seg3, tri1));
    BOOST_CHECK(!boost::geometry::crosses(seg4, tri1));
    BOOST_CHECK(boost::geometry::crosses(seg5, tri1));
    BOOST_CHECK(!boost::geometry::crosses(seg6, tri1));

    BOOST_CHECK(!boost::geometry::within(seg1, tri1));
    BOOST_CHECK(!boost::geometry::within(seg2, tri1));
    BOOST_CHECK(!boost::geometry::within(seg3, tri1));
    BOOST_CHECK(boost::geometry::within(seg4, tri1));
    BOOST_CHECK(!boost::geometry::within(seg5, tri1));
    BOOST_CHECK(!boost::geometry::within(seg6, tri1));
  }
#endif
  /*}}}*/
  /*factory test{{{*/
#ifdef TEST_FACTORY
  {
    Point2D p1(0.0f, 0.0f), p2(4.0f, 3.0f), p3(0.0f, 3.0f), p4(4.0f, 0.0f),
      p5(0.0f, 5.0f), p6(0.0f, -1.5f), p7(4.0f, 4.5f);

    std::unique_ptr<Shape> v1 = Shape::getFactory().create("vertex", {p2});
    BOOST_CHECK(v1);
    std::unique_ptr<Shape> seg1 =
      Shape::getFactory().create("segment", {p6, p7});
    BOOST_CHECK(seg1);
    std::unique_ptr<Shape> tri1 =
      Shape::getFactory().create("triangle", {p1, p2, p3});
    BOOST_CHECK(tri1);
    std::unique_ptr<Shape> quad1 =
      Shape::getFactory().create("Quadrangle", {p1, p3, p2, p4});
    BOOST_CHECK(quad1);
    std::vector<Point2D> ans;
    boost::geometry::intersection(seg1->toSegment(), quad1->toPolygon(), ans);
    BOOST_CHECK(ans.size() == 2);
    BOOST_CHECK(ans.front() == Point2D(3.f, 3.f));
    BOOST_CHECK(ans.back() == Point2D(1.f, 0.f));
  }
#endif
  /*}}}*/
  /*shape function test{{{*/
#ifdef TEST_SHAPE_FUNCTION
  {
    Point2D p1(0.0f, 0.0f), p2(4.0f, 3.0f), p3(0.0f, 3.0f), p4(4.0f, 0.0f),
      p5(0.0f, 5.0f), p6(0.0f, -1.5f), p7(4.0f, 4.5f);
    std::unique_ptr<Shape> v1 = Shape::getFactory().create("Vertex", {p2});
    BOOST_CHECK(v1);
    std::unique_ptr<Shape> seg1 =
      Shape::getFactory().create("Segment", {p6, p7});
    BOOST_CHECK(seg1);
    std::unique_ptr<Shape> tri1 =
      Shape::getFactory().create("Triangle", {p1, p2, p3});
    BOOST_CHECK(tri1);
    std::unique_ptr<Shape> quad1 =
      Shape::getFactory().create("Quadrangle", {p1, p3, p2, p4});
    BOOST_CHECK(quad1);
    std::vector<Point2D> ans;
    boost::geometry::intersection(seg1->toSegment(), quad1->toPolygon(), ans);
    BOOST_CHECK(ans.size() == 2);
    BOOST_CHECK(ans.front() == Point2D(3.f, 3.f));
    BOOST_CHECK(ans.back() == Point2D(1.f, 0.f));
  }
#endif
  /*}}}*/
}
